/** @file    FileSystem.cpp
 *  @author  Alessandro Polo
 *  @version $Id: FileSystem.cpp 2854 2010-08-06 20:48:09Z alex $
 *  @brief
 * File containing methods for the wosh::FileSystem class.
 * The header for this class can be found in FileSystem.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <core/FileSystem.h>

 #include <core/ObjectAllocator.h>
 #include <core/WoshKernel.h>
 #include <core/Settings.h>
 #include <core/MethodsCommon.h>
 #include <core/List.h>
 #include <core/Table.h>
 #include <core/Utilities.h>
 #include <fstream>

#ifdef _OS_POSIX
# include <dirent.h>
#else
# include <core/win32/utils.h>
# include <core/win32/dirent.h>
#endif
# ifdef _OS_WINCE
#  include <winbase.h>
#  include <core/wince/stringutils.h>
#else
# include <sys/types.h>
# include <sys/stat.h>
# include <stdio.h> //for remove( ) and rename( )
#endif

#ifndef MAX_PATH
# define MAX_PATH	256
#endif


using namespace std;
namespace wosh {

// Gamma singleton: static reference is allocated and maintained by WoshKernel
extern "C" {
	FileSystem* FileSystem::getInstance() { 
		if ( WoshKernel::getInstance() == NULL ) return NULL;
		return &WoshKernel::getInstance()->filesystem();
	}
}
 // register as abstract because it is a singleton:
 WOSH_REGISTER_INTERFACE(wosh::FileSystem, "wosh::WoshModule", 0.01, _static_FileSystem )

FileSystem::tStrStrMap FileSystem::fsAliases;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

FileInfo::FileInfo( const FileInfo& m )
	: local_path(m.local_path), wosh_path(m.wosh_path), type(m.type),
	time_created(m.time_created), time_modified(m.time_modified), size(m.size) { }

FileInfo& FileInfo::operator=(const FileInfo& m) {
	if (this == &m) return *this; // same object?
	this->local_path = m.local_path;
	this->wosh_path = m.wosh_path;
	this->type = m.type;
	this->time_created = m.time_created;
	this->time_modified = m.time_modified;
	this->size = m.size;
	return *this;
 }
bool FileInfo::operator==(const FileInfo &other) const {
	if ( this->local_path != other.local_path ) return false;

	return true;
 }

std::string FileInfo::toString() const {
	return Utilities::format("%s[%s]", getClassNameOnlyOf(getClassName()), this->local_path.c_str() );
 }

bool FileInfo::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = true;
	ret = hash_context->update( this->local_path ) & ret;
	ret = hash_context->update( this->wosh_path ) & ret;
	ret = hash_context->update( (long)this->type ) & ret;
	ret = hash_context->update( this->time_created ) & ret;
	ret = hash_context->update( this->time_modified ) & ret;
	ret = hash_context->update( this->size ) & ret;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

FileSystem::FileSystem( const WoshKernel* parent )
	: WoshModule(parent, _FileSystem_NAME, _FileSystem_URI) {

	Log(LOG_DEBUG, " AutoDetecting WOSH root.." );
	setWoshRoot("");

	Log(LOG_DEBUG, " AutoDefining common Aliases [$CONFIG,$DATABASE,$TEMP].." );
	FileSystem::fsAliases.set("$CONFIG", "$ROOT" _FileSystem_PathSeparator "etc" _FileSystem_PathSeparator "wosh" );
	FileSystem::fsAliases.set("$DATABASE", "$ROOT" _FileSystem_PathSeparator "var" _FileSystem_PathSeparator "database" );
	FileSystem::fsAliases.set("$TEMP", "$ROOT" _FileSystem_PathSeparator "tmp" );

	Log(LOG_DEBUG, " Registering Methods.." );
	MethodRequest* mmListAliases = Methods.create<MethodRequest>( _METHOD_List, "List Aliases", Permission(Permission::RX) );
	mmListAliases->setMethod( this, (MethodRequestPtr)&FileSystem::mmDoListAliases );
	MethodRequest* mmSetAlias = Methods.create<MethodRequest>( _FileSystem_METHOD_set_alias, "Set Alias", Permission::R_XR_XR__() );
	mmSetAlias->setMethod( this, (MethodRequestPtr)&FileSystem::mmDoSetAlias );
	MethodRequest* mmRemoveAlias = Methods.create<MethodRequest>( _FileSystem_METHOD_rem_alias, "Remove Alias", Permission::R_XR_XR__() );
	mmRemoveAlias->setMethod( this, (MethodRequestPtr)&FileSystem::mmDoRemoveAlias );

	MethodRequest* mmDirTree = Methods.create<MethodRequest>( _FileSystem_METHOD_dir_tree, "get files of a dir", Permission::R_XR_XR__() );
	mmDirTree->setMethod( this, (MethodRequestPtr)&FileSystem::mmDoDirTree );

	MethodRequest* mmFileDelete = Methods.create<MethodRequest>( _FileSystem_METHOD_file_delete, "Delete File", Permission::R_XR_XR__() );
	mmFileDelete->setMethod( this, (MethodRequestPtr)&FileSystem::mmDoFileDelete );
	MethodRequest* mmFileRename = Methods.create<MethodRequest>( _FileSystem_METHOD_file_rename, "Rename (move) File", Permission::R_XR_XR__() );
	mmFileRename->setMethod( this, (MethodRequestPtr)&FileSystem::mmDoFileRename );
	MethodRequest* mmFileSize = Methods.create<MethodRequest>( _FileSystem_METHOD_file_size, "get File size", Permission::R_XR_XR__() );
	mmFileSize->setMethod( this, (MethodRequestPtr)&FileSystem::mmDoFileSize );
	MethodRequest* mmFileStat = Methods.create<MethodRequest>( _FileSystem_METHOD_file_stat, "get File timestamp", Permission::R_XR_XR__() );
	mmFileStat->setMethod( this, (MethodRequestPtr)&FileSystem::mmDoFileStat );
 }

FileSystem::~FileSystem() {
	Log(LOG_DEBUG, ":~FileSystem() Destroying.." );

	Log(LOG_VERBOSE, " Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS

WRESULT FileSystem::onKernelInitializing() {
	WRESULT ret = WoshModule::onKernelInitializing();

	if ( Properties.exists(_FileSystem_KEY_WoshRoot) )
		setWoshRoot( Properties.getValueOf(_FileSystem_KEY_WoshRoot).toString() );

	if ( Properties.exists(_FileSystem_KEY_Aliases) ) {
		vector<string> aliases;
		string aliasesStr = Properties.getValueOf(_FileSystem_KEY_Aliases).toString();
		Utilities::splitString(aliasesStr, ";", aliases, false, true);
		std::vector<std::string>::iterator it;
		for ( it=aliases.begin(); it!=aliases.end(); ++it ) {
			vector<string> mapping;
			Utilities::splitString(*it, "|", mapping, false, true);
			if ( mapping.size() < 2 ) continue;
			this->registerAlias(mapping.at(0), mapping.at(1) );
		 }
	 }
	return ret;
 }

/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// STATIC METHODS

WRESULT FileSystem::deleteFile( const std::string& filepath ) {
#ifdef _OS_WINCE 
	wchar_t wFilePath [MAX_PATH];
	mbstowcs (wFilePath,filepath.c_str(),MAX_PATH);
	if ( DeleteFile( wFilePath ) )
		return WRET_OK;
#else
	if ( remove(filepath.c_str()) == 0 )
		return WRET_OK;
#endif
	return WRET_ERR_PARAM;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT FileSystem::renameFile( const std::string& orig_filepath, const std::string& new_filepath ) {
#ifdef _OS_WINCE
	wchar_t wFilePathOrig[MAX_PATH];
	mbstowcs (wFilePathOrig,orig_filepath.c_str(),MAX_PATH);
	wchar_t wFilePathNew [MAX_PATH];
	mbstowcs (wFilePathNew,new_filepath.c_str(),MAX_PATH);
	if ( MoveFile( wFilePathOrig, wFilePathNew ) )
		return WRET_OK;
#else
	if ( rename(orig_filepath.c_str(), new_filepath.c_str()) == 0 )
		return WRET_OK;
#endif
	return WRET_ERR_PARAM;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int64 FileSystem::getTimeStampOf( const std::string& filepath, short field ) {
#ifdef _OS_WINCE
	wchar_t wFilePath [MAX_PATH];
	mbstowcs (wFilePath,filepath.c_str(),MAX_PATH);
	HANDLE hFile = CreateFile(wFilePath,0,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,INVALID_HANDLE_VALUE);
	if ( hFile == INVALID_HANDLE_VALUE )
		return 0;
	FILETIME writeTime;
	if ( GetFileTime(hFile,NULL,NULL,&writeTime) == 0 ) {
		CloseHandle(hFile);
		return 0;
	 }
	int64 value = (((long)writeTime.dwHighDateTime) << 32) | writeTime.dwLowDateTime;
	CloseHandle(hFile);
	return value;
#else
# ifdef _OS_WIN32
	struct _stat fileInfo;
	if ( _stat(filepath.c_str(), &fileInfo) != 0 )
		return 0;
# else
	struct stat fileInfo;
	if ( stat(filepath.c_str(), &fileInfo) != 0 )
		return 0;
# endif
	switch(field) {
		case 1:		return fileInfo.st_ctime;
		case 2:		return fileInfo.st_atime;
		default: 
		case 0:		return fileInfo.st_mtime;
	 }
#endif
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int64 FileSystem::getSizeOf( const std::string& filepath ) {
#ifdef _OS_WINCE
	wchar_t wFilePath [MAX_PATH];
	mbstowcs (wFilePath,filepath.c_str(),MAX_PATH);
	HANDLE hFile = CreateFile(wFilePath,0,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,INVALID_HANDLE_VALUE);
	if ( hFile == INVALID_HANDLE_VALUE )
		return 0;
	DWORD dwSizeHigh = 0; DWORD dwError = 0;
	DWORD dwSizeLow = GetFileSize(hFile, &dwSizeHigh);
	if (dwSizeLow == 0xFFFFFFFF && (dwError = GetLastError()) != NO_ERROR ) {
		CloseHandle(hFile);
		return 0;
	 }
	int64 value = (((long)dwSizeHigh) << 32) | dwSizeLow;
	CloseHandle(hFile);
	return value;
#else
# ifdef _OS_WIN32
	struct _stat fileInfo;
	if ( _stat(filepath.c_str(), &fileInfo) != 0 )
		return 0;
# else
	struct stat fileInfo;
	if ( stat(filepath.c_str(), &fileInfo) != 0 )
		return 0;
# endif
	return fileInfo.st_size;
#endif
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

short FileSystem::getTypeOf( const std::string& filepath ) {
#ifdef _OS_WINCE
	WOSH_DEPRECATE( "FileSystem::getTypeOf", "NOT IMPLEMENTED WINCE" )
	return 0;
#else
	DIR *dp;
	if ( (dp = opendir(filepath.c_str())) != NULL ) {
		closedir(dp);
		return 2;
	 }
	return 0;
#endif
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT FileSystem::getFileInfo( FileInfo& info, const std::string& filepath ) {
	info.local_path = filepath;
	info.time_modified = getTimeStampOf(filepath, 0);
	info.time_created = getTimeStampOf(filepath, 1);
	info.type = getTypeOf(filepath);
	info.size = getSizeOf(filepath);
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT FileSystem::saveTextAs( const std::string& filepath, const char* data, int64 size, bool append ) {
	if ( data == NULL || size <= 0 ) return WRET_ERR_PARAM;
	ofstream myfile;
	if ( append )
		myfile.open(filepath.c_str(), ios::out | ios::app); 
	else
		myfile.open(filepath.c_str(), ios::out); 
	if ( !myfile.is_open() )
		return WRET_ERR_ILLEGAL_USE;

	myfile.write(data, size);
	myfile.close();
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT FileSystem::getDirNodeOf( TreeNode* dirNode, const std::string& dir, int recursive_level, const std::string& pattern ) {
	if ( dirNode == NULL ) return WRET_ERR_PARAM;
	FilePath fDir;
	fDir.set(dir);
	string localDir = fDir.getPathLocal();
	FileInfo* info = new FileInfo();
	WRESULT ret = getFileInfo( *info, localDir );
	info->wosh_path = dir;
	dirNode->setContent(info);
	if ( recursive_level <= 0 )
		return ret;

#ifdef _OS_WINCE
	WOSH_DEPRECATE( "FileSystem::getFilesOf", "NOT IMPLEMENTED WINCE" )
#else
	DIR *dp;
	if ( (dp = opendir(localDir.c_str())) == NULL )
		return WRET_ERR_PARAM;
	localDir += _FileSystem_PathSeparatorLocal;

	struct dirent *dirp;
	while ( (dirp = readdir(dp)) != NULL ) {
		string filename(dirp->d_name);
		if ( filename == "." || filename == ".." )
			continue;
		if ( !pattern.empty() ) {
			if ( filename.find(pattern) == std::string::npos )
				continue;
		 }
		TreeNode* childNode = new TreeNode(dirNode);
		ret += getDirNodeOf( childNode, localDir + filename, recursive_level-1, pattern );
	 }
	closedir(dp);
#endif
	return ret;
 }

Tree* FileSystem::getTreeOf( const std::string& dir, int recursive_level, const std::string& pattern ) {
	Tree* tree = new Tree();
	tree->setTreeName( Utilities::format("Tree of %s [%s]", dir.c_str(), pattern.c_str()) );
	FilePath fDir;
	fDir.set(dir);
	string localDir = fDir.getPathLocal() + _FileSystem_PathSeparatorLocal;
	TreeNode* rooNode = new TreeNode(NULL);
	WRESULT ret = getDirNodeOf( rooNode, dir, recursive_level, pattern );
	if ( WFAILED(ret) ) {
		delete rooNode;
		return tree;
	 }
	tree->setRoot(rooNode);
	return tree;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

// get also dirs
WRESULT FileSystem::getFilesOf( const std::string& dir, vector<string>& files, bool recursive, bool full_path, const std::string& pattern ) {
	FilePath fDir;
	fDir.set(dir);
	string localDir = fDir.getPathLocal() + _FileSystem_PathSeparatorLocal;

#ifdef _OS_WINCE
	wchar_t wDir[MAX_PATH];
	mbstowcs(wDir,dir.c_str(), MAX_PATH);
	WIN32_FIND_DATA findFileData;
	HANDLE hFind;
	hFind = FindFirstFile(wDir, &findFileData);
	if ( hFind == INVALID_HANDLE_VALUE )
		return WRET_ERR_PARAM;
	char tmp[MAX_PATH];

	while(true) {

		wcstombs(tmp, findFileData.cFileName, MAX_PATH );
		string filename(tmp);
		if ( filename == "." || filename == ".." )
			continue;
		if ( !pattern.empty() ) {
			if ( filename.find(pattern) == std::string::npos )
				continue;
		 }
		if ( full_path )
			files.push_back( localDir + filename );
		else
			files.push_back( filename );

		if ( findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
			if ( recursive ) {
				getFilesOf(localDir + filename, files, true, full_path, pattern);
			 }
		 }

		if ( FindNextFile(hFind,&findFileData) == FALSE ) {
			break;
		 }
	 }
	FindClose(hFind);
	WOSH_DEPRECATE( "FileSystem::getFilesOf", "NOT IMPLEMENTED WINCE" )
	return WRET_ERR_INTERNAL;
#else
	DIR *dp;
	DIR *dpc;
	struct dirent *dirp;
	if ( (dp = opendir(localDir.c_str())) == NULL )
		return WRET_ERR_PARAM;

	while ( (dirp = readdir(dp)) != NULL ) {
		string filename(dirp->d_name);
		if ( filename == "." || filename == ".." )
			continue;
		if ( !pattern.empty() ) {
			if ( filename.find(pattern) == std::string::npos )
				continue;
		 }
		if ( full_path )
			files.push_back( localDir + filename );
		else
			files.push_back( filename );

		if ( (dpc = opendir( (localDir + filename).c_str())) != NULL ) {
			closedir(dpc);
			if ( recursive )
				getFilesOf(localDir + filename, files, true, full_path, pattern);
		 }
	 }
	closedir(dp);
	return WRET_OK;
#endif
 }

std::string FileSystem::getCurrentWorkingDirectory() {
#ifdef _OS_POSIX
	char* buffer = new char[MAX_PATH];
	memset(buffer, 0, MAX_PATH);
	getcwd(buffer, MAX_PATH-1);
	string restr(buffer);
	delete [] buffer;
	FilePath::fixPath(restr);
	FilePath::toLocalPath(restr);
	return restr; // should be $ROOT/bin
#else
	WCHAR wpath[MAX_PATH];
	memset(wpath, 0, MAX_PATH);
	GetModuleFileName(NULL, (LPWSTR)&wpath, MAX_PATH-1);
	string strAppExe;
	ConvertToString( wpath, strAppExe);
	FilePath::fixPath(strAppExe);
	strAppExe = FilePath::getFolder(strAppExe);
	FilePath::toLocalPath(strAppExe);
	return strAppExe; // should be $ROOT/bin
#endif
 }


WRESULT FileSystem::reserveTempFile( std::string& filename, const std::string& filename_tip ) {
	FilePath temp;
	int64 rnd_id = Utilities::randomInt64(); rnd_id = _GET_Abs(rnd_id);
	temp.set( Utilities::format("$TEMP/%s.%"PRId64".temp", filename_tip.c_str(), getAlias("$TEMP").c_str(), rnd_id ) );
	filename = temp.toPathLocal(true);
	return WRET_OK;
 }

WRESULT FileSystem::freeTempFile( const std::string& full_wosh_path ) {
	return deleteFile(full_wosh_path);
 }


////////////////////////////////////////////////////////////////////////////////////////////// STATIC METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT FileSystem::setWoshRoot( const std::string& root_path ) {
	this->woshRootPath = root_path;
	FilePath::fixPath(this->woshRootPath);
	FilePath::toLocalPath(this->woshRootPath);
	if ( this->woshRootPath.empty() ) {
		string strAppDirectory = FileSystem::getCurrentWorkingDirectory(); // should be $ROOT/bin
		this->woshRootPath = FilePath::getFolder(strAppDirectory); // should be $ROOT
		Log(LOG_INFO, ":setWoshRoot(%s) Guessing '%s' as WOSH Root", root_path.c_str(), this->woshRootPath.c_str() );
	 }
	Log(LOG_INFO, ":setWoshRoot(%s) : Applied '%s' as $ROOT", root_path.c_str(), this->woshRootPath.c_str() );
	return registerAlias("$ROOT", this->woshRootPath);
 }

WRESULT FileSystem::registerAlias( const std::string& alias, const std::string& full_path ) {
	FileSystem::fsAliases.transactionBeginWrite();
	FileSystem::fsAliases.set(alias, full_path);
	FileSystem::fsAliases.transactionEnd();
	return WRET_OK;
 }

WRESULT FileSystem::unRegisterAlias( const std::string& alias ) {
	MutexLockerWrite mL(FileSystem::fsAliases.getMutex());
	return FileSystem::fsAliases.remove(alias);
 }

string FileSystem::getAlias( const std::string& alias ) {
	MutexLockerRead mL(FileSystem::fsAliases.getMutex());
	if ( FileSystem::fsAliases.exists(alias) )
		return FileSystem::fsAliases.find(alias);
	return "";
 }

bool FileSystem::resolveAlias( std::string& path_step ) {
	MutexLockerRead mL(FileSystem::fsAliases.getMutex());
	if ( !FileSystem::fsAliases.exists(path_step) )
		return false;
	path_step = FileSystem::fsAliases.find(path_step);
	return true;
 }

bool FileSystem::resolveLocalPath( const std::string& local_path, std::string& wosh_path ) {
	string lpath_final = local_path;
	unsigned int fsize = 0;
	FileSystem::fsAliases.transactionBeginRead();
	tStrStrMap::ConstIterator it;
	const tStrStrMap::ConstIterator it_end = FileSystem::fsAliases.end();
	for ( it=FileSystem::fsAliases.begin(); it!=it_end; ++it ) {
		string path = it->second;
		FilePath::resolve(path);
		FilePath::toLocalPath(path);
		if ( local_path.find(path) != 0 ) continue;
		if ( fsize > path.size() ) continue;
		fsize = path.size();

		vector<string> items;
		Utilities::splitString(local_path, _FileSystem_PathSeparatorLocal, items, true, true);
		lpath_final = Utilities::joinVector(items, _FileSystem_PathSeparator);
		wosh_path = it->first;
		wosh_path += lpath_final.substr( fsize );
	 }
	FileSystem::fsAliases.transactionEnd();
	if ( fsize == 0 )
		return false;
	FilePath::fixPath(wosh_path);
	return true;
 }

////////////////////////////////////////////////////////////////////////////////////////////// STATIC METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* FileSystem::mmDoFileDelete( const Request* request ) {
	if ( request == NULL ) return NULL;
	string filename = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoFileDelete(%s)", filename.c_str() );
	return request->replyResponse( deleteFile(filename) );
 }

Response* FileSystem::mmDoFileRename( const Request* request ) {
	if ( request == NULL ) return NULL;
	Variant dFileNameOld; Variant dFileNameNew;
	request->extractArguments(dFileNameOld, dFileNameNew);
	if ( !dFileNameOld.isStringNotEmpty() || !dFileNameNew.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid arguments [filename_old, filename_new]");
	Log(LOG_VERBOSE, ":mmDoFileRename(%s, %s)", dFileNameOld.asString().c_str(), dFileNameNew.asString().c_str() );
	return request->replyResponse( renameFile(dFileNameOld.asString(), dFileNameNew.asString()) );
 }

Response* FileSystem::mmDoFileStat( const Request* request ) {
	if ( request == NULL ) return NULL;
	Variant dFileName; Variant dField;
	request->extractArguments(dFileName, dField);
	if ( !dFileName.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid arguments [filename]");
	int64 epoch = getTimeStampOf(dFileName.asString() , dField.toInteger(0) );
	return request->replyResponse(WRET_OK, new Variant(epoch, Variant::DATETIME) );
 }

Response* FileSystem::mmDoFileSize( const Request* request ) {
	if ( request == NULL ) return NULL;
	string filename = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoFileSize(%s)", filename.c_str() );
	int64 size = getSizeOf(filename);
	return request->replyResponse(WRET_OK, new Variant(size) );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* FileSystem::mmDoDirTree( const Request* request ) {
	if ( request == NULL ) return NULL;
	string dirname = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoDirTree(%s)", dirname.c_str() );
	Tree* tree = getTreeOf(dirname);
	return request->replyResponse(WRET_OK, tree );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* FileSystem::mmDoSetAlias( const Request* request ) {
	if ( request == NULL ) return NULL;
	Variant dName; Variant dValue;
	request->extractArguments(dName, dValue);
	if ( !dName.isStringNotEmpty() || !dValue.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid arguments [name, value]");
	Log(LOG_VERBOSE, ":mmDoSetAlias() %s => %s", dName.asString().c_str(), dValue.asString().c_str() );
	FileSystem::fsAliases.transactionBeginWrite();
	FileSystem::fsAliases.set(dName.asString().c_str(), dValue.asString().c_str() );
	FileSystem::fsAliases.transactionEnd();
	return request->replyResponse(WRET_OK);
 }

Response* FileSystem::mmDoRemoveAlias( const Request* request ) {
	if ( request == NULL ) return NULL;
	Variant dName;
	request->extractArguments(dName);
	if ( !dName.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid arguments [name]");
	Log(LOG_VERBOSE, ":mmDoRemoveAlias(%s)", dName.asString().c_str() );
	FileSystem::fsAliases.transactionBeginWrite();
	if ( !FileSystem::fsAliases.exists(dName.asString().c_str()) ) {
		FileSystem::fsAliases.transactionEnd();
		return request->replyResponse(WRET_ERR_PARAM, "Alias-Name not found");
	 }
	FileSystem::fsAliases.erase(dName.asString().c_str());
	FileSystem::fsAliases.transactionEnd();
	return request->replyResponse(WRET_OK);
 }

Response* FileSystem::mmDoListAliases( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoListAliases(%s)", option.c_str() );
	if ( option == "s" || option == "short" ) {
		FileSystem::fsAliases.transactionBeginRead();
		List* objs = new List(FileSystem::fsAliases.size());
		tStrStrMap::ConstIterator it;
		const tStrStrMap::ConstIterator it_end = FileSystem::fsAliases.end();
		for ( it=FileSystem::fsAliases.begin(); it!=it_end; ++it ) {
			objs->push_back<Variant>( it->first );
		 }
		FileSystem::fsAliases.transactionEnd();
		return request->replyResponse(WRET_OK, objs);
	 }
	else {
		FileSystem::fsAliases.transactionBeginRead();
		Table* tbl = new Table( FileSystem::fsAliases.size(), 3, true );
		tbl->setTableName("Aliases");
		tbl->setHeaderHorizontal("Name", 0);
		tbl->setHeaderHorizontal("Value", 1);
		tbl->setHeaderHorizontal("Resolved (Local) Path", 2);
		unsigned int iRow = 0;
		tStrStrMap::ConstIterator it;
		const tStrStrMap::ConstIterator it_end = FileSystem::fsAliases.end();
		for ( it=FileSystem::fsAliases.begin(); it!=it_end; ++it, ++iRow ) {
			tbl->set<Variant>( it->first, iRow, 0);
			tbl->set<Variant>( it->second, iRow, 1);
			string path = it->second;
			FilePath::resolve(path);
			FilePath::toLocalPath(path);
			tbl->set<Variant>( path, iRow, 2);
		 }
		FileSystem::fsAliases.transactionEnd();
		return request->replyResponse(WRET_OK, tbl);
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
